﻿using System;
using System.IO;
using System.Security.Cryptography;

using System.Text;

namespace vJine.Core {
    public class Crypto {
        public class Asymmetric {
            #region Asymmetric
            #region Key

            public static string GenKey() {
                System.Security.Cryptography.RSACryptoServiceProvider rsa =
                    new System.Security.Cryptography.RSACryptoServiceProvider();

                string Key = Convert.ToBase64String(rsa.ExportCspBlob(true));

                rsa.Clear();
                rsa = null;

                return Key;
            }

            public static string GenPubKey(string Key) {
                System.Security.Cryptography.RSACryptoServiceProvider rsa =
                    new System.Security.Cryptography.RSACryptoServiceProvider();
                rsa.ImportCspBlob(Convert.FromBase64String(Key));

                string PubKey = Convert.ToBase64String(rsa.ExportCspBlob(false));

                rsa.Clear();
                rsa = null;

                return PubKey;
            }
            #endregion Key

            #region Sigh
            public static string Sign(string Key, string Data) {
                return
                    Sign<MD5CryptoServiceProvider>(
                        Convert.FromBase64String(Key),
                        Encoding.UTF8.GetBytes(Data)
                        );
            }

            public static string SignF(string Key, string File) {
                string FileHash = MD5F(File);

                string Hash = Sign(Key, FileHash);

                return Hash;
            }

            public static string Sign(string Key, Stream Data) {
                return Sign<MD5CryptoServiceProvider>(
                        Convert.FromBase64String(Key),
                        Data
                        );
            }

            public static string Sign<THash>(byte[] Key, byte[] Data)
                where THash : HashAlgorithm, new() {
                return Sign<THash>(Key, Data, 0, Data.Length);
            }

            public static string Sign<THash>(byte[] Key, Stream Data)
                where THash : HashAlgorithm, new() {
                string StreamHash =
                    Utility.BytesToHex(Hash<THash>(Data));

                string Signature =
                    Sign<THash>(Key, Utility.HexToBytes(StreamHash));

                return Signature;
            }

            public static string Sign<THash>(byte[] Key, byte[] Data, int offset, int count)
                where THash : HashAlgorithm, new() {
                System.Security.Cryptography.RSACryptoServiceProvider rsa =
                    new System.Security.Cryptography.RSACryptoServiceProvider();
                rsa.ImportCspBlob(Key);

                string HashValue =
                    Utility.BytesToHex(rsa.SignData(Data, new THash()));

                rsa.Clear();
                rsa = null;

                return HashValue;
            }
            #endregion Sign

            #region Verify
            public static bool Verify(string PubKey, string Data, string Hash) {
                return
                    Verify<MD5CryptoServiceProvider>(
                    Convert.FromBase64String(PubKey),
                    Encoding.UTF8.GetBytes(Data),
                    Hash
                    );
            }

            public static bool VerifyF(string PubKey, string File, string Hash) {
                string FileHash = MD5F(File);

                bool IsVerified = Verify(PubKey, FileHash, Hash);

                return IsVerified;
            }

            public static bool Verify(string PubKey, Stream Data, string Hash) {
                return
                    Verify<MD5CryptoServiceProvider>(
                        Convert.FromBase64String(PubKey),
                        Data,
                        Hash
                        );
            }

            public static bool Verify<THash>(byte[] PubKey, byte[] Data, string Hash)
              where THash : HashAlgorithm, new() {
                return Verify<THash>(PubKey, Data, 0, Data.Length, Hash);
            }


            public static bool Verify<THash>(byte[] PubKey, Stream Data, string Hash)
                where THash : HashAlgorithm, new() {
                string StreamHash = Utility.BytesToHex(Hash<THash>(Data));

                bool IsVerified =
                    Verify<THash>(PubKey, Utility.HexToBytes(StreamHash), Hash);

                return IsVerified;
            }

            public static bool Verify<THash>(byte[] PubKey, byte[] Data, int offset, int count, string Hash)
               where THash : HashAlgorithm, new() {
                RSACryptoServiceProvider rsa =
                    new RSACryptoServiceProvider();
                rsa.ImportCspBlob(PubKey);
                bool IsVerified =
                    rsa.VerifyData(Utility.ToSubArray<byte>(Data, offset, count), new THash(), Utility.HexToBytes(Hash));

                rsa.Clear(); rsa = null;

                return IsVerified;
            }
            #endregion Verify

            public static string Encrypt(string PubKey, string Data) {
                return Convert.ToBase64String(
                    Encrypt(Convert.FromBase64String(PubKey), Encoding.UTF8.GetBytes(Data))
                    );
            }

            public static byte[] Encrypt(byte[] PubKey, byte[] Data) {
                RSACryptoServiceProvider rsa =
                    new RSACryptoServiceProvider();
                rsa.ImportCspBlob(PubKey);

                byte[] eData = rsa.Encrypt(Data, false);

                rsa.Clear();
                rsa = null;

                return eData;
            }

            public static string Decrypt(string Key, string Data) {
                return Encoding.UTF8.GetString(
                    Decrypt(Convert.FromBase64String(Key), Convert.FromBase64String(Data))
                    );
            }

            public static byte[] Decrypt(byte[] Key, byte[] Data) {
                RSACryptoServiceProvider rsa =
                    new RSACryptoServiceProvider();
                rsa.ImportCspBlob(Key);

                byte[] dData = rsa.Decrypt(Data, false);

                rsa.Clear();
                rsa = null;

                return dData;
            }
            #endregion Asymmetric
        }

        public class Symmetric {
            #region Symmetric

            #region Single Password
            public static string Encrypt(string password, string V) {
                return Encrypt<RijndaelManaged>(password, V);
            }

            public static byte[] Encrypt(string password, byte[] V) {
                return Encrypt<RijndaelManaged>(password, V);
            }

            public static Stream Encrypt(string password, Stream stream) {
                return Encrypt<RijndaelManaged>(password, stream);
            }

            public static string Decrypt(string password, string V) {
                return Decrypt<RijndaelManaged>(password, V);
            }

            public static byte[] Decrypt(string password, byte[] V) {
                return Decrypt<RijndaelManaged>(password, V);
            }

            public static Stream Decrypt(string password, Stream stream) {
                return Decrypt<RijndaelManaged>(password, stream);
            }
            #endregion Single Password

            #region Key and IV
            public static string Encrypt(string Key, string IV, string V) {
                return Encrypt<RijndaelManaged>(Key, IV, V);
            }

            public static byte[] Encrypt(string Key, string IV, byte[] V) {
                return Encrypt<RijndaelManaged>(Key, IV, V);
            }

            public static Stream Encrypt(string Key, string IV, Stream stream) {
                return Encrypt<RijndaelManaged>(Key, IV, stream);
            }

            public static string Decrypt(string Key, string IV, string V) {
                return Decrypt<RijndaelManaged>(Key, IV, V);
            }

            public static byte[] Decrypt(string Key, string IV, byte[] V) {
                return Decrypt<RijndaelManaged>(Key, IV, V);
            }

            public static Stream Decrypt(string Key, string IV, Stream stream) {
                return Decrypt<RijndaelManaged>(Key, IV, stream);
            }
            #endregion Key and IV

            #region <T> Single Password
            private static void GenKeyAndIvByPassword<T>(T cryptor, string password)
                where T : SymmetricAlgorithm, new() {
                cryptor.GenerateKey();
                cryptor.GenerateIV();
                byte[] KEY = Encoding.UTF8.GetBytes(MD5(password));
                int lKey = KEY.Length;
                while (KEY.Length < cryptor.Key.Length) {
                    Array.Resize<byte>(ref KEY, KEY.Length + lKey);
                    Array.Copy(Encoding.UTF8.GetBytes(MD5(password)), 0, KEY, KEY.Length - lKey, lKey);
                }
                byte[] IV = Encoding.UTF8.GetBytes(MD5(password));
                while (IV.Length < cryptor.IV.Length) {
                    Array.Resize<byte>(ref IV, IV.Length + lKey);
                    Array.Copy(Encoding.UTF8.GetBytes(MD5(password)), 0, IV, IV.Length - lKey, lKey);
                }
                Array.Reverse(IV);

                Array.Resize<byte>(ref KEY, cryptor.Key.Length);
                Array.Resize<byte>(ref IV, cryptor.IV.Length);
                cryptor.Key = KEY;
                cryptor.IV = IV;
            }

            public static string Encrypt<T>(string password, string V)
                where T : SymmetricAlgorithm, new() {
                return Convert.ToBase64String(
                    Encrypt<T>(password, Encoding.UTF8.GetBytes(V))
                    );
            }

            public static byte[] Encrypt<T>(string password, byte[] V)
                where T : SymmetricAlgorithm, new() {
                T t = new T();
                GenKeyAndIvByPassword<T>(t, password);

                ICryptoTransform ict = t.CreateEncryptor();
                byte[] FV = ict.TransformFinalBlock(V, 0, V.Length);
                ict.Dispose();
                t = null;

                return FV;
            }

            public static Stream Encrypt<T>(string password, Stream stream) where T : SymmetricAlgorithm, new() {
                T t = new T();
                GenKeyAndIvByPassword<T>(t, password);

                CryptoStream cs =
                    new CryptoStream(stream, t.CreateEncryptor(), CryptoStreamMode.Write);
                return cs;
            }

            public static string Decrypt<T>(string password, string V) where T : SymmetricAlgorithm, new() {
                return Encoding.UTF8.GetString(
                    Decrypt<T>(password, Convert.FromBase64String(V))
                    );
            }

            public static byte[] Decrypt<T>(string password, byte[] V) where T : SymmetricAlgorithm, new() {
                T t = new T();
                GenKeyAndIvByPassword<T>(t, password);

                ICryptoTransform ict = t.CreateDecryptor();
                byte[] FV = ict.TransformFinalBlock(V, 0, V.Length);
                ict.Dispose();
                t = null;

                return FV;
            }

            public static Stream Decrypt<T>(string password, Stream stream) where T : SymmetricAlgorithm, new() {
                T t = new T();
                GenKeyAndIvByPassword<T>(t, password);

                CryptoStream cs =
                    new CryptoStream(stream, t.CreateDecryptor(), CryptoStreamMode.Read);
                return cs;
            }
            #endregion <T> Single Password

            #region <T> Key and IV

            public static string Encrypt<T>(string Key, string IV, string V) where T : SymmetricAlgorithm, new() {
                return Convert.ToBase64String(
                    Encrypt<T>(Key, IV, Encoding.UTF8.GetBytes(V))
                    );
            }

            public static byte[] Encrypt<T>(string Key, string IV, byte[] V) where T : SymmetricAlgorithm, new() {
                T t = new T();
                t.Key = Convert.FromBase64String(Key);
                t.IV = Convert.FromBase64String(IV);

                ICryptoTransform ict = t.CreateEncryptor();
                byte[] FV = ict.TransformFinalBlock(V, 0, V.Length);
                ict.Dispose();
                t = null;

                return FV;
            }

            public static Stream Encrypt<T>(string Key, string IV, Stream stream) where T : SymmetricAlgorithm, new() {
                T t = new T();
                t.Key = Convert.FromBase64String(Key);
                t.IV = Convert.FromBase64String(IV);
                CryptoStream cs =
                    new CryptoStream(stream, t.CreateEncryptor(), CryptoStreamMode.Write);
                return cs;
            }

            public static string Decrypt<T>(string Key, string IV, string V) where T : SymmetricAlgorithm, new() {
                return Encoding.UTF8.GetString(
                    Decrypt<T>(Key, IV, Convert.FromBase64String(V))
                    );
            }

            public static byte[] Decrypt<T>(string Key, string IV, byte[] V) where T : SymmetricAlgorithm, new() {
                T t = new T();
                t.Key = Convert.FromBase64String(Key);
                t.IV = Convert.FromBase64String(IV);

                ICryptoTransform ict = t.CreateDecryptor();
                byte[] FV = ict.TransformFinalBlock(V, 0, V.Length);
                ict.Dispose();
                t = null;

                return FV;
            }

            public static Stream Decrypt<T>(string Key, string IV, Stream stream) where T : SymmetricAlgorithm, new() {
                T t = new T();
                t.Key = Convert.FromBase64String(Key);
                t.IV = Convert.FromBase64String(IV);
                CryptoStream cs =
                    new CryptoStream(stream, t.CreateDecryptor(), CryptoStreamMode.Read);
                return cs;
            }
            #endregion <T> Key and IV

            #endregion Symmetric
        }

        #region Hash

        public static string MD5(string Data) {
            return Utility.BytesToHex(Hash<MD5CryptoServiceProvider>(Data));
        }

        public static string MD5L(string Data) {
            return MD5(Data).Substring(16, 16);
        }

        public static string MD5F(string File) {
            FileStream fs =
                new FileStream(File, FileMode.Open, FileAccess.Read, FileShare.Read);

            string Hash = Utility.BytesToHex(Hash<MD5CryptoServiceProvider>(fs));

            fs.Close();
            fs.Dispose();
            fs = null;
            return Hash;
        }

        public static string MD5FL(string File) {
            return MD5F(File).Substring(16, 16);
        }

        public static string MD5(Stream Data) {
            long position = Data.Position;
            try {
                return Utility.BytesToHex(Hash<MD5CryptoServiceProvider>(Data));
            } finally {
                Data.Position = position;
            }

        }

        public static string MD5L(Stream Data) {
            return MD5(Data).Substring(16, 16);
        }

        public static string MD5(byte[] Data) {
            return Utility.BytesToHex(Hash<MD5CryptoServiceProvider>(Data));
        }

        public static string MD5L(byte[] Data) {
            return MD5(Data).Substring(16, 16);
        }

        public static string SHA1(string Data) {
            return Utility.BytesToHex(Hash<SHA1CryptoServiceProvider>(Data));
        }

        public static string SHA1F(string File) {
            FileStream fs =
                new FileStream(File, FileMode.Open, FileAccess.Read, FileShare.Read);

            string Hash = Utility.BytesToHex(Hash<SHA1CryptoServiceProvider>(fs));

            fs.Close();
            fs.Dispose();
            fs = null;
            return Hash;
        }

        public string SHA1(Stream Stream) {
            return Utility.BytesToHex(Hash<SHA1CryptoServiceProvider>(Stream));
        }

        public string SHA1(byte[] B) {
            return Utility.BytesToHex(Hash<SHA1CryptoServiceProvider>(B));
        }

        public static byte[] Hash<T>(string V) where T : HashAlgorithm, new() {
            T tHash = new T();

            byte[] Buffer =
                Encoding.UTF8.GetBytes(V);

            byte[] Hash = tHash.ComputeHash(Buffer, 0, Buffer.Length);

            tHash.Clear();
            tHash = null;

            return Hash;
        }

        public static byte[] Hash<T>(Stream V) where T : HashAlgorithm, new() {
            T tHash = new T();

            byte[] Hash = tHash.ComputeHash(V);

            tHash.Clear();
            tHash = null;

            return Hash;
        }

        public static byte[] Hash<T>(byte[] V) where T : HashAlgorithm, new() {
            T tHash = new T();

            byte[] Hash = tHash.ComputeHash(V);

            tHash.Clear();
            tHash = null;

            return Hash;
        }
        #endregion Hash
    }
}
